package com.rjs.serialization;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;

public class ProtoStuffSerializer implements ISerializer {
	
	private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
	private static Objenesis objenesis = new ObjenesisStd(true);
	
	@SuppressWarnings("unchecked")
	private static <T> Schema<T> getSchema(Class<T> clzz) {
		Schema<T> schema = (Schema<T>)cachedSchema.get(clzz);
		if (schema == null) {
			schema = RuntimeSchema.createFrom(clzz);
		}
		return schema;
	}

	@SuppressWarnings("unchecked")
	public <T> byte[] serialize(T obj) throws IOException {
		Class<T> clzz = (Class<T>)obj.getClass();
		LinkedBuffer linkedBuffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		Schema<T> schema = getSchema(clzz);
		try {
			return ProtobufIOUtil.toByteArray(obj, schema, linkedBuffer);
		} finally {
			linkedBuffer.clear();
		}
	}

	public <T> T deserialize(byte[] buf, Class<T> clzz) throws IOException, ClassNotFoundException {
		T message = objenesis.newInstance(clzz);
		Schema<T> schema = getSchema(clzz);
		ProtobufIOUtil.mergeFrom(buf, message, schema);
		return message;
	}

}
